Here's another fun and confusing crash:
[adiumx.git] / Frameworks / Adium Framework / IconFamily.m
blob3b9ba99e332b7a17dfcbddc342cd3ba040d51b43
1 // IconFamily.m
2 // IconFamily class implementation
3 // by Troy Stephens, Thomas Schnitzer, David Remahl, Nathan Day and Ben Haller
4 // version 0.5.1
5 //
6 // Project Home Page:
7 //   http://homepage.mac.com/troy_stephens/software/objects/IconFamily/
8 //
9 // Problems, shortcomings, and uncertainties that I'm aware of are flagged
10 // with "NOTE:".  Please address bug reports, bug fixes, suggestions, etc.
11 // to me at troy_stephens@mac.com
13 // This code is provided as-is, with no warranty, in the hope that it will be
14 // useful.  However, it appears to work fine on Mac OS X 10.1.5 and 10.2. :-)
16 //IconFamily depends heavily upon Carbon calls and thus will only work in Mac OS X
17 #ifdef MAC_OS_X_VERSION_10_0
19 #import "IconFamily.h"
20 #import "NSString+CarbonFSSpecCreation.h"
22 @interface IconFamily (Internals)
24 + (NSImage*) resampleImage:(NSImage*)image toIconWidth:(int)width usingImageInterpolation:(NSImageInterpolation)imageInterpolation;
26 + (Handle) get32BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;
28 + (Handle) get8BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;
30 + (Handle) get8BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;
32 + (Handle) get1BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize;
34 - (BOOL) addResourceType:(OSType)type asResID:(int)resID;
36 @end
38 @implementation IconFamily
40 + (IconFamily*) iconFamily
42     return [[[IconFamily alloc] init] autorelease];
45 + (IconFamily*) iconFamilyWithContentsOfFile:(NSString*)path
47     return [[[IconFamily alloc] initWithContentsOfFile:path] autorelease];
50 + (IconFamily*) iconFamilyWithIconOfFile:(NSString*)path
52     return [[[IconFamily alloc] initWithIconOfFile:path] autorelease];
55 + (IconFamily*) iconFamilyWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily
57     return [[[IconFamily alloc] initWithIconFamilyHandle:hNewIconFamily] autorelease];
60 + (IconFamily*) iconFamilyWithSystemIcon:(int)fourByteCode
62     return [[[IconFamily alloc] initWithSystemIcon:fourByteCode] autorelease];
65 + (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image
67     return [[[IconFamily alloc] initWithThumbnailsOfImage:image] autorelease];
70 + (IconFamily*) iconFamilyWithThumbnailsOfImage:(NSImage*)image usingImageInterpolation:(NSImageInterpolation)imageInterpolation
72     return [[[IconFamily alloc] initWithThumbnailsOfImage:image usingImageInterpolation:imageInterpolation] autorelease];
75 // This is IconFamily's designated initializer.  It creates a new IconFamily that initially has no elements.
77 // The proper way to do this is to simply allocate a zero-sized handle (not to be confused with an empty handle) and assign it to hIconFamily.  This technique works on Mac OS X 10.2 as well as on 10.0.x and 10.1.x.  Our previous technique of allocating an IconFamily struct with a resourceSize of 0 no longer works as of Mac OS X 10.2.
78 - init
80     if((self = [super init]))
81         {
82         hIconFamily = (IconFamilyHandle) NewHandle( 0 );
83         if (hIconFamily == NULL) {
84             [self autorelease];
85             return nil;
86         }
87     }
88         
89     return self;
92 - initWithContentsOfFile:(NSString*)path
94     FSSpec fsSpec;
95     OSErr result;
96     
97     self = [self init];
98     if (self) {
99         if (hIconFamily) {
100             DisposeHandle( (Handle)hIconFamily );
101             hIconFamily = NULL;
102         }
103                 if (![path getFSSpec:&fsSpec createFileIfNecessary:NO]) {
104                         [self autorelease];
105                         return nil;
106                 }
107                 result = ReadIconFile( &fsSpec, &hIconFamily );
108                 if (result != noErr) {
109                         [self autorelease];
110                         return nil;
111                 }
112     }
113     return self;
116 - initWithIconFamilyHandle:(IconFamilyHandle)hNewIconFamily
118     self = [self init];
119     if (self) {
120         if (hIconFamily) {
121             DisposeHandle( (Handle)hIconFamily );
122             hIconFamily = NULL;
123         }
124         // NOTE: Do we have to somehow "retain" the handle
125         //       (increment its reference count)?
126         hIconFamily = hNewIconFamily;
127     }
128     return self;
131 - initWithIconOfFile:(NSString*)path
133     IconRef     iconRef;
134     OSErr       result;
135     SInt16      label;
136     FSSpec      fileSpec;
138     self = [self init];
139     if (self)
140     {
141         if (hIconFamily)
142         {
143             DisposeHandle( (Handle)hIconFamily );
144             hIconFamily = NULL;
145         }
147         if( ![path getFSSpec:&fileSpec createFileIfNecessary:NO] )
148         {
149             [self autorelease];
150             return nil;
151         }
153         result = GetIconRefFromFile(
154                                     &fileSpec,
155                                     &iconRef,
156                                     &label );
158         if (result != noErr)
159         {
160             [self autorelease];
161             return nil;
162         }
164         result = IconRefToIconFamily(
165                                      iconRef,
166                                      kSelectorAllAvailableData,
167                                      &hIconFamily );
169         if (result != noErr || !hIconFamily)
170         {
171             [self autorelease];
172             return nil;
173         }
175         ReleaseIconRef( iconRef );
176     }
177     return self;
180 - initWithSystemIcon:(int)fourByteCode
182     IconRef     iconRef;
183     OSErr       result;
185     self = [self init];
186     if (self)
187     {
188         if (hIconFamily)
189         {
190             DisposeHandle( (Handle)hIconFamily );
191             hIconFamily = NULL;
192         }
194         result = GetIconRef(kOnSystemDisk, kSystemIconsCreator, fourByteCode, &iconRef);
196         if (result != noErr)
197         {
198             [self autorelease];
199             return nil;
200         }
202         result = IconRefToIconFamily(
203                                      iconRef,
204                                      kSelectorAllAvailableData,
205                                      &hIconFamily );
207         if (result != noErr || !hIconFamily)
208         {
209             [self autorelease];
210             return nil;
211         }
213         ReleaseIconRef( iconRef );
214     }
215     return self;
218 - initWithThumbnailsOfImage:(NSImage*)image
220     // The default is to use a high degree of antialiasing, producing a smooth image.
221     return [self initWithThumbnailsOfImage:image usingImageInterpolation:NSImageInterpolationHigh];
224 - initWithThumbnailsOfImage:(NSImage*)image usingImageInterpolation:(NSImageInterpolation)imageInterpolation
226     NSImage* iconImage128x128;
227     NSImage* iconImage32x32;
228     NSImage* iconImage16x16;
229     NSBitmapImageRep* iconBitmap128x128;
230     NSBitmapImageRep* iconBitmap32x32;
231     NSBitmapImageRep* iconBitmap16x16;
232     NSImage* bitmappedIconImage128x128;
233     
234     // Start with a new, empty IconFamily.
235     self = [self init];
236     if (self == nil)
237         return nil;
238     
239     // Resample the given image to create a 128x128 pixel, 32-bit RGBA
240     // version, and use that as our "thumbnail" (128x128) icon and mask.
241     //
242     // Our +resampleImage:toIconWidth:... method, in its present form,
243     // returns an NSImage that contains an NSCacheImageRep, rather than
244     // an NSBitmapImageRep.  We convert to an NSBitmapImageRep, so that
245         // our methods can scan the image data, using initWithFocusedViewRect:.
246     iconImage128x128 = [IconFamily resampleImage:image toIconWidth:128 usingImageInterpolation:imageInterpolation];
247         [iconImage128x128 lockFocus];
248         iconBitmap128x128 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, 128, 128)];
249         [iconImage128x128 unlockFocus];
250     if (iconBitmap128x128) {
251         [self setIconFamilyElement:kThumbnail32BitData fromBitmapImageRep:iconBitmap128x128];
252         [self setIconFamilyElement:kThumbnail8BitMask  fromBitmapImageRep:iconBitmap128x128];
253     }
255     // Create an NSImage with the iconBitmap128x128 NSBitmapImageRep, that we
256     // can resample to create the smaller icon family elements.  (This is
257     // most likely more efficient than resampling from the original image again,
258     // particularly if it is large.  It produces a slightly different result, but
259     // the difference is minor and should not be objectionable...)
260     bitmappedIconImage128x128 = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
261     [bitmappedIconImage128x128 addRepresentation:iconBitmap128x128];
262    
263     // Resample the 128x128 image to create a 32x32 pixel, 32-bit RGBA version,
264     // and use that as our "large" (32x32) icon and 8-bit mask.
265     iconImage32x32 = [IconFamily resampleImage:bitmappedIconImage128x128 toIconWidth:32 usingImageInterpolation:imageInterpolation];
266         [iconImage32x32 lockFocus];
267         iconBitmap32x32 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, 32, 32)];
268         [iconImage32x32 unlockFocus];
269     if (iconBitmap32x32) {
270         [self setIconFamilyElement:kLarge32BitData fromBitmapImageRep:iconBitmap32x32];
271         [self setIconFamilyElement:kLarge8BitData fromBitmapImageRep:iconBitmap32x32];
272         [self setIconFamilyElement:kLarge8BitMask fromBitmapImageRep:iconBitmap32x32];
273         [self setIconFamilyElement:kLarge1BitMask fromBitmapImageRep:iconBitmap32x32];
274     }
276     // Resample the 128x128 image to create a 16x16 pixel, 32-bit RGBA version,
277     // and use that as our "small" (16x16) icon and 8-bit mask.
278     iconImage16x16 = [IconFamily resampleImage:bitmappedIconImage128x128 toIconWidth:16 usingImageInterpolation:imageInterpolation];
279         [iconImage16x16 lockFocus];
280         iconBitmap16x16 = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, 16, 16)];
281         [iconImage16x16 unlockFocus];
282     if (iconBitmap16x16) {
283         [self setIconFamilyElement:kSmall32BitData fromBitmapImageRep:iconBitmap16x16];
284         [self setIconFamilyElement:kSmall8BitData fromBitmapImageRep:iconBitmap16x16];
285         [self setIconFamilyElement:kSmall8BitMask fromBitmapImageRep:iconBitmap16x16];
286         [self setIconFamilyElement:kSmall1BitMask fromBitmapImageRep:iconBitmap16x16];
287     }
289     // Release all of the images that we created and no longer need.
290     [bitmappedIconImage128x128 release];
291     [iconBitmap128x128 release];
292     [iconBitmap32x32 release];
293     [iconBitmap16x16 release];
295     // Return the new icon family!
296     return self;
299 - (void) dealloc
301     DisposeHandle( (Handle)hIconFamily );
302     [super dealloc];
305 - (NSBitmapImageRep*) bitmapImageRepWithAlphaForIconFamilyElement:(OSType)elementType;
307     NSBitmapImageRep* bitmapImageRep;
308     int pixelsWide;
309     Handle hRawBitmapData;
310     Handle hRawMaskData;
311     OSType maskElementType;
312     OSErr result;
313     unsigned long* pRawBitmapData;
314     unsigned long* pRawBitmapDataEnd;
315     char* pRawMaskData;
316     unsigned char* pBitmapImageRepBitmapData;
318     // Make sure elementType is a valid type that we know how to handle, and
319     // figure out the dimensions and bit depth of the bitmap for that type.
320     switch (elementType) {
321         // 'it32' 128x128 32-bit RGB image
322         case kThumbnail32BitData:
323                 maskElementType = kThumbnail8BitMask;
324                 pixelsWide = 128;
325             break;
326             
327         // 'il32' 32x32 32-bit RGB image
328         case kLarge32BitData:
329                 maskElementType = kLarge8BitMask;
330                 pixelsWide = 32;
331             break;
332             
333         // 'is32' 16x16 32-bit RGB image
334         case kSmall32BitData:
335                 maskElementType = kSmall8BitMask;
336                 pixelsWide = 16;
337             break;
338             
339         default:
340             return nil;
341     }
343     // Get the raw, uncompressed bitmap data for the requested element.
344     hRawBitmapData = NewHandle( pixelsWide * pixelsWide * 4 );
345     result = GetIconFamilyData( hIconFamily, elementType, hRawBitmapData );
346     if (result != noErr) {
347         DisposeHandle( hRawBitmapData );
348         return nil;
349     }
350         
351     // Get the corresponding raw, uncompressed 8-bit mask data.
352     hRawMaskData = NewHandle( pixelsWide * pixelsWide );
353     result = GetIconFamilyData( hIconFamily, maskElementType, hRawMaskData );
354     if (result != noErr) {
355         DisposeHandle( hRawMaskData );
356         hRawMaskData = NULL;
357     }
358     
359     // The retrieved raw bitmap data is stored at 32 bits per pixel: 3 bytes
360     // for the RGB color of each pixel, plus an extra unused byte.  We can
361     // therefore fold the mask data into the color data in-place (though
362     // getting the proper byte ordering requires some bit-shifting).
363     HLock( hRawBitmapData );
364     pRawBitmapData = (unsigned long*) *hRawBitmapData;
365     pRawBitmapDataEnd = pRawBitmapData + pixelsWide * pixelsWide;
366     if (hRawMaskData) {
367         HLock( hRawMaskData );
368         pRawMaskData = *hRawMaskData;
369         while (pRawBitmapData < pRawBitmapDataEnd) {
370             *pRawBitmapData = (*pRawBitmapData << 8) | *pRawMaskData++;
371             ++pRawBitmapData;
372         }
373         HUnlock( hRawMaskData );
374     } else {
375         while (pRawBitmapData < pRawBitmapDataEnd) {
376             *pRawBitmapData = (*pRawBitmapData << 8) | 0xff;
377             ++pRawBitmapData;
378         }
379     }
380     
381     // Create a new NSBitmapImageRep with the given bitmap data.  Note that
382     // when creating the NSBitmapImageRep we pass in NULL for the "planes"
383     // parameter.  This causes the new NSBitmapImageRep to allocate its own
384     // buffer for the bitmap data (which it will own and release when the
385     // NSBitmapImageRep is released), rather than referencing the bitmap
386     // data we pass in (which will soon disappear when we call
387     // DisposeHandle() below!).  (See the NSBitmapImageRep documentation for
388     // the -initWithBitmapDataPlanes:... method, where this is explained.)
389     //
390     // Once we have the new NSBitmapImageRep, we get a pointer to its
391     // bitmapData and copy our bitmap data in.
392     bitmapImageRep = [[[NSBitmapImageRep alloc]
393         initWithBitmapDataPlanes:NULL
394                       pixelsWide:pixelsWide
395                       pixelsHigh:pixelsWide
396                    bitsPerSample:8
397                  samplesPerPixel:4
398                         hasAlpha:YES
399                         isPlanar:NO
400                   colorSpaceName:NSDeviceRGBColorSpace // NOTE: is this right?
401                      bytesPerRow:0
402                     bitsPerPixel:0] autorelease];
403     pBitmapImageRepBitmapData = [bitmapImageRep bitmapData];
404     if (pBitmapImageRepBitmapData) {
405         memcpy( pBitmapImageRepBitmapData, *hRawBitmapData,
406                 pixelsWide * pixelsWide * 4 );
407     }
408     HUnlock( hRawBitmapData );
409                       
410     // Free the retrieved raw data.
411     DisposeHandle( hRawBitmapData );
412     if (hRawMaskData)
413         DisposeHandle( hRawMaskData );
415     // Return nil if the NSBitmapImageRep didn't give us a buffer to copy into.
416     if (pBitmapImageRepBitmapData == NULL)
417         return nil;
419     // Return the new NSBitmapImageRep.
420     return bitmapImageRep;
423 - (NSImage*) imageWithAllReps
425     NSImage* image = NULL;
426     image = [[[NSImage alloc] initWithData:[NSData dataWithBytes:*hIconFamily length:GetHandleSize((Handle)hIconFamily)]] autorelease];
428     return image;
430     //investigate optimisations (dataWithBytesNoCopy:length: for example...)
433 - (BOOL) setIconFamilyElement:(OSType)elementType fromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep
435     Handle hRawData = NULL;
436     OSErr result;
438     switch (elementType) {
439         // 'it32' 128x128 32-bit RGB image
440         case kThumbnail32BitData:
441             hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:128];
442             break;
443             
444         // 't8mk' 128x128 8-bit alpha mask
445         case kThumbnail8BitMask:
446             hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:128];
447             break;
448             
449         // 'il32' 32x32 32-bit RGB image
450         case kLarge32BitData:
451             hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
452             break;
453             
454         // 'l8mk' 32x32 8-bit alpha mask
455         case kLarge8BitMask:
456             hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
457             break;
458             
459         // 'ICN#' 32x32 1-bit alpha mask
460         case kLarge1BitMask:
461             hRawData = [IconFamily get1BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
462             break;
463             
464         // 'icl8' 32x32 8-bit indexed image data
465         case kLarge8BitData:
466                 hRawData = [IconFamily get8BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:32];
467                 break;
469         // 'is32' 16x16 32-bit RGB image
470         case kSmall32BitData:
471                 hRawData = [IconFamily get32BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
472                 break;
473             
474         // 's8mk' 16x16 8-bit alpha mask
475         case kSmall8BitMask:
476             hRawData = [IconFamily get8BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
477             break;
478             
479         // 'ics#' 16x16 1-bit alpha mask
480         case kSmall1BitMask:
481             hRawData = [IconFamily get1BitMaskFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
482             break;
484         // 'ics8' 16x16 8-bit indexed image data
485         case kSmall8BitData:
486                 hRawData = [IconFamily get8BitDataFromBitmapImageRep:bitmapImageRep requiredPixelSize:16];
487                 break;
488             
489         default:
490             return NO;
491     }
492         
493     if (hRawData == NULL)
494         {
495                 NSLog(@"Null data returned to setIconFamilyElement:fromBitmapImageRep:");
496                 return NO;
497         }
498         
499     result = SetIconFamilyData( hIconFamily, elementType, hRawData );
500     DisposeHandle( hRawData );
501         
502     if (result != noErr)
503         {
504                 NSLog(@"SetIconFamilyData() returned error %d", result);
505                 return NO;
506         }
507         
508     return YES;
511 - (BOOL) setAsCustomIconForFile:(NSString*)path
513     return( [self setAsCustomIconForFile:path withCompatibility:NO] );
516 - (BOOL) setAsCustomIconForFile:(NSString*)path withCompatibility:(BOOL)compat
518     FSSpec targetFileFSSpec;
519     FSRef targetFileFSRef;
520     FSRef parentDirectoryFSRef;
521     SInt16 file;
522     OSErr result;
523     FInfo finderInfo;
524     Handle hExistingCustomIcon;
525     Handle hIconFamilyCopy;
526         NSDictionary *fileAttributes;
527         OSType existingType = kUnknownType, existingCreator = kUnknownType;
528         
529     // Get an FSRef and an FSSpec for the target file, and an FSRef for its parent directory that we can use in the FNNotify() call below.
530     if (![path getFSRef:&targetFileFSRef createFileIfNecessary:NO])
531                 return NO;
532     result = FSGetCatalogInfo( &targetFileFSRef, kFSCatInfoNone, NULL, NULL, &targetFileFSSpec, &parentDirectoryFSRef );
533     if (result != noErr)
534         return NO;
535         
536     // Get the file's type and creator codes.
537         fileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:NO];
538         if (fileAttributes)
539         {
540                 existingType = [fileAttributes fileHFSTypeCode];
541                 existingCreator = [fileAttributes fileHFSCreatorCode];
542     }
543         
544     // Make sure the file has a resource fork that we can open.  (Although
545     // this sounds like it would clobber an existing resource fork, the Carbon
546     // Resource Manager docs for this function say that's not the case.  If
547     // the file already has a resource fork, we receive a result code of
548     // dupFNErr, which is not really an error per se, but just a notification
549     // to us that creating a new resource fork for the file was not necessary.)
550     FSpCreateResFile( &targetFileFSSpec, existingCreator, existingType, smRoman );
551     result = ResError();
552     if (!(result == noErr || result == dupFNErr))
553                 return NO;
554     
555     // Open the file's resource fork.
556     file = FSpOpenResFile( &targetFileFSSpec, fsRdWrPerm );
557     if (file == -1)
558                 return NO;
559         
560     // Make a copy of the icon family data to pass to AddResource().
561     // (AddResource() takes ownership of the handle we pass in; after the
562     // CloseResFile() call its master pointer will be set to 0xffffffff.
563     // We want to keep the icon family data, so we make a copy.)
564     // HandToHand() returns the handle of the copy in hIconFamily.
565     hIconFamilyCopy = (Handle) hIconFamily;
566     result = HandToHand( &hIconFamilyCopy );
567     if (result != noErr) {
568         CloseResFile( file );
569         return NO;
570     }
571     
572     // Remove the file's existing kCustomIconResource of type kIconFamilyType
573     // (if any).
574     hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource );
575     if( hExistingCustomIcon )
576         RemoveResource( hExistingCustomIcon );
577     
578     // Now add our icon family as the file's new custom icon.
579     AddResource( (Handle)hIconFamilyCopy, kIconFamilyType,
580                  kCustomIconResource, "\p");
581     if (ResError() != noErr) {
582         CloseResFile( file );
583         return NO;
584     }
585     
586     if( compat )
587     {
588         [self addResourceType:kLarge8BitData asResID:kCustomIconResource];
589         [self addResourceType:kLarge1BitMask asResID:kCustomIconResource];
590         [self addResourceType:kSmall8BitData asResID:kCustomIconResource];
591         [self addResourceType:kSmall1BitMask asResID:kCustomIconResource];
592     }
593         
594     // Close the file's resource fork, flushing the resource map and new icon
595     // data out to disk.
596     CloseResFile( file );
597     if (ResError() != noErr)
598                 return NO;
599         
600     // Now we need to set the file's Finder info so the Finder will know that
601     // it has a custom icon.  Start by getting the file's current finder info:
602     result = FSpGetFInfo( &targetFileFSSpec, &finderInfo );
603     if (result != noErr)
604                 return NO;
605     
606     // Set the kHasCustomIcon flag, and clear the kHasBeenInited flag.
607     //
608     // From Apple's "CustomIcon" code sample:    
609     //     "set bit 10 (has custom icon) and unset the inited flag
610     //      kHasBeenInited is 0x0100 so the mask will be 0xFEFF:"
611     //    finderInfo.fdFlags = 0xFEFF & (finderInfo.fdFlags | kHasCustomIcon ) ;
612     finderInfo.fdFlags = (finderInfo.fdFlags | kHasCustomIcon ) & ~kHasBeenInited;
613         
614     // Now write the Finder info back.
615     result = FSpSetFInfo( &targetFileFSSpec, &finderInfo );
616     if (result != noErr)
617                 return NO;
618         
619     // Notify the system that the directory containing the file has changed, to give Finder the chance to find out about the file's new custom icon.
620     result = FNNotify( &parentDirectoryFSRef, kFNDirectoryModifiedMessage, kNilOptions );
621     if (result != noErr)
622         return NO;
623         
624     return YES;
627 + (BOOL) removeCustomIconFromFile:(NSString*)path
629     FSSpec targetFileFSSpec;
630     FSRef targetFileFSRef;
631     FSRef parentDirectoryFSRef;
632     SInt16 file;
633     OSErr result;
634     FInfo finderInfo;
635     Handle hExistingCustomIcon;
637     // Get an FSRef and an FSSpec for the target file, and an FSRef for its parent directory that we can use in the FNNotify() call below.
638     if (![path getFSRef:&targetFileFSRef createFileIfNecessary:NO])
639                 return NO;
640     result = FSGetCatalogInfo( &targetFileFSRef, kFSCatInfoNone, NULL, NULL, &targetFileFSSpec, &parentDirectoryFSRef );
641     if (result != noErr)
642         return NO;
643         
644     // Open the file's resource fork, if it has one.
645     file = FSpOpenResFile( &targetFileFSSpec, fsRdWrPerm );
646     if (file == -1)
647         return NO;
649     // Remove the file's existing kCustomIconResource of type kIconFamilyType
650     // (if any).
651     hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource );
652     if( hExistingCustomIcon )
653         RemoveResource( hExistingCustomIcon );
655     // Close the file's resource fork, flushing the resource map out to disk.
656     CloseResFile( file );
657     if (ResError() != noErr)
658         return NO;
660     // Now we need to set the file's Finder info so the Finder will know that
661     // it has no custom icon.  Start by getting the file's current finder info:
662     result = FSpGetFInfo( &targetFileFSSpec, &finderInfo );
663     if (result != noErr)
664         return NO;
666     // Clear the kHasCustomIcon flag and the kHasBeenInited flag.
667     finderInfo.fdFlags = finderInfo.fdFlags & ~(kHasCustomIcon | kHasBeenInited);
669     // Now write the Finder info back.
670     result = FSpSetFInfo( &targetFileFSSpec, &finderInfo );
671     if (result != noErr)
672         return NO;
674     // Notify the system that the directory containing the file has changed, to give Finder the chance to find out about the file's new custom icon.
675     result = FNNotify( &parentDirectoryFSRef, kFNDirectoryModifiedMessage, kNilOptions );
676     if (result != noErr)
677         return NO;
678         
679     return YES;
682 - (BOOL) setAsCustomIconForDirectory:(NSString*)path
684     return [self setAsCustomIconForDirectory:path withCompatibility:NO];
687 - (BOOL) setAsCustomIconForDirectory:(NSString*)path withCompatibility:(BOOL)compat
689     NSFileManager *fm = [NSFileManager defaultManager];
690     BOOL isDir;
691     BOOL exists;
692     NSString *iconrPath = [path stringByAppendingPathComponent:@"Icon\r"];
693     FSSpec targetFileFSSpec, targetFolderFSSpec;
694     FSRef targetFolderFSRef;
695     SInt16 file;
696     OSErr result;
697     FInfo finderInfo;
698     FSCatalogInfo catInfo;
699     Handle hExistingCustomIcon;
700     Handle hIconFamilyCopy;
702     exists = [fm fileExistsAtPath:path isDirectory:&isDir];
704     if( !isDir || !exists )
705         return NO;
707     if( [fm fileExistsAtPath:iconrPath] )
708     {
709         if( ![fm removeFileAtPath:iconrPath handler:nil] )
710             return NO;
711     }
713     if (![iconrPath getFSSpec:&targetFileFSSpec createFileIfNecessary:YES])
714         return NO;
716     if( ![path getFSSpec:&targetFolderFSSpec createFileIfNecessary:YES] )
717         return NO;
719     if( ![path getFSRef:&targetFolderFSRef createFileIfNecessary:NO] )
720         return NO;
722     // Make sure the file has a resource fork that we can open.  (Although
723     // this sounds like it would clobber an existing resource fork, the Carbon
724     // Resource Manager docs for this function say that's not the case.)
725     FSpCreateResFile( &targetFileFSSpec, kUnknownType, kUnknownType, smRoman );
726     if (ResError() != noErr)
727         return NO;
729     // Open the file's resource fork.
730     file = FSpOpenResFile( &targetFileFSSpec, fsRdWrPerm );
731     if (file == -1)
732         return NO;
734     // Make a copy of the icon family data to pass to AddResource().
735     // (AddResource() takes ownership of the handle we pass in; after the
736     // CloseResFile() call its master pointer will be set to 0xffffffff.
737     // We want to keep the icon family data, so we make a copy.)
738     // HandToHand() returns the handle of the copy in hIconFamily.
739     hIconFamilyCopy = (Handle) hIconFamily;
740     result = HandToHand( &hIconFamilyCopy );
741     if (result != noErr) {
742         CloseResFile( file );
743         return NO;
744     }
746     // Remove the file's existing kCustomIconResource of type kIconFamilyType
747     // (if any).
748     hExistingCustomIcon = GetResource( kIconFamilyType, kCustomIconResource );
749     if( hExistingCustomIcon )
750         RemoveResource( hExistingCustomIcon );
752     // Now add our icon family as the file's new custom icon.
753     AddResource( (Handle)hIconFamilyCopy, kIconFamilyType,
754                  kCustomIconResource, "\p");
756     if (ResError() != noErr) {
757         CloseResFile( file );
758         return NO;
759     }
761     if( compat )
762     {
763         [self addResourceType:kLarge8BitData asResID:kCustomIconResource];
764         [self addResourceType:kLarge1BitMask asResID:kCustomIconResource];
765         [self addResourceType:kSmall8BitData asResID:kCustomIconResource];
766         [self addResourceType:kSmall1BitMask asResID:kCustomIconResource];
767     }
769     // Close the file's resource fork, flushing the resource map and new icon
770     // data out to disk.
771     CloseResFile( file );
772     if (ResError() != noErr)
773         return NO;
775     // Make folder icon file invisible
776     result = FSpGetFInfo( &targetFileFSSpec, &finderInfo );
777     if (result != noErr)
778         return NO;
779     finderInfo.fdFlags = (finderInfo.fdFlags | kIsInvisible ) & ~kHasBeenInited;
780     // And write info back
781     result = FSpSetFInfo( &targetFileFSSpec, &finderInfo );
782     if (result != noErr)
783         return NO;
785     result = FSGetCatalogInfo( &targetFolderFSRef,
786                                kFSCatInfoFinderInfo,
787                                &catInfo, nil, nil, nil);
788     if( result != noErr )
789         return NO;
791     ((DInfo*)catInfo.finderInfo)->frFlags = ( ((DInfo*)catInfo.finderInfo)->frFlags | kHasCustomIcon ) & ~kHasBeenInited;
793     FSSetCatalogInfo( &targetFolderFSRef,
794                       kFSCatInfoFinderInfo,
795                       &catInfo);
796     if( result != noErr )
797         return NO;
799     // Notify the system that the target directory has changed, to give Finder the chance to find out about its new custom icon.
800     result = FNNotify( &targetFolderFSRef, kFNDirectoryModifiedMessage, kNilOptions );
801     if (result != noErr)
802         return NO;
803         
804     return YES;
807 /*- (BOOL) writeToFile:(NSString*)path
809     FSSpec fsSpec;
810     OSErr result;
811     
812     if (![path getFSSpec:&fsSpec createFileIfNecessary:YES])
813         return NO;
814     result = WriteIconFile( hIconFamily, &fsSpec );
815     if (result != noErr)
816         return NO;
817         
818     return YES;
819 } This method has a problem with files not representable as an FSSpec.*/
821 - (BOOL) writeToFile:(NSString*)path
823     NSData* iconData = NULL;
825     HLock((Handle)hIconFamily);
826     
827     iconData = [NSData dataWithBytes:*hIconFamily length:GetHandleSize((Handle)hIconFamily)];
828     [iconData writeToFile:path atomically:NO];
830     HUnlock((Handle)hIconFamily);
832     return YES;
835 @end
837 @implementation IconFamily (Internals)
839 + (NSImage*) resampleImage:(NSImage*)image toIconWidth:(int)iconWidth usingImageInterpolation:(NSImageInterpolation)imageInterpolation
841     NSGraphicsContext* graphicsContext;
842     BOOL wasAntialiasing;
843     NSImageInterpolation previousImageInterpolation;
844     NSImage* newImage = nil;
845 //    NSBitmapImageRep* newBitmapImageRep;
846 //    unsigned char* bitmapData;
847 //    NSImageRep* originalImageRep;
848     NSImage* workingImage;
849     NSImageRep* workingImageRep;
850     NSSize size, pixelSize, newSize;
851     NSRect iconRect;
852     NSRect targetRect;
854     // Create a working copy of the image and scale its size down to fit in
855     // the square area of the icon.
856     //
857     // It seems like there should be a more memory-efficient alternative to
858     // first duplicating the entire original image, but I don't know what it
859     // is.  We need to change some properties ("size" and "scalesWhenResized")
860     // of the original image, but we shouldn't change the original, so a copy
861     // is necessary.
862     workingImage = [image copyWithZone:[image zone]];
863     [workingImage setScalesWhenResized:YES];
864     size = [workingImage size];
865     workingImageRep = [workingImage bestRepresentationForDevice:nil];
866     if ([workingImageRep isKindOfClass:[NSBitmapImageRep class]]) {
867         pixelSize.width  = [workingImageRep pixelsWide];
868         pixelSize.height = [workingImageRep pixelsHigh];
869         if (!NSEqualSizes( size, pixelSize )) {
870             [workingImage setSize:pixelSize];
871             [workingImageRep setSize:pixelSize];
872             size = pixelSize;
873         }
874     }
875     if (size.width >= size.height) {
876         newSize.width  = iconWidth;
877         newSize.height = floor( (float) iconWidth * size.height / size.width + 0.5 );
878     } else {
879         newSize.height = iconWidth;
880         newSize.width  = floor( (float) iconWidth * size.width / size.height + 0.5 );
881     }
882     [workingImage setSize:newSize];
884 #if 1   // This is the way that works.  It gives the newImage an NSCachedImageRep.
885         
886         if(iconWidth != 0){
887                 // Create a new image the size of the icon, and clear it to transparent.
888                 newImage = [[NSImage alloc] initWithSize:NSMakeSize(iconWidth,iconWidth)];
889                 [newImage lockFocus];
890                 iconRect.origin.x = iconRect.origin.y = 0;
891                 iconRect.size.width = iconRect.size.height = iconWidth;
892                 [[NSColor clearColor] set];
893                 NSRectFill( iconRect );
894                 
895                 // Set current graphics context to use antialiasing and high-quality
896                 // image scaling.
897                 graphicsContext = [NSGraphicsContext currentContext];
898                 wasAntialiasing = [graphicsContext shouldAntialias];
899                 previousImageInterpolation = [graphicsContext imageInterpolation];
900                 [graphicsContext setShouldAntialias:YES];
901                 [graphicsContext setImageInterpolation:imageInterpolation];
902                 
903                 // Composite the working image into the icon bitmap, centered.
904                 targetRect.origin.x = ((float)iconWidth - newSize.width ) / 2.0;
905                 targetRect.origin.y = ((float)iconWidth - newSize.height) / 2.0;
906                 targetRect.size.width = newSize.width;
907                 targetRect.size.height = newSize.height;
908                 [workingImageRep drawInRect:targetRect];
909                 
910                 // Restore previous graphics context settings.
911                 [graphicsContext setShouldAntialias:wasAntialiasing];
912                 [graphicsContext setImageInterpolation:previousImageInterpolation];
913                 
914                 [newImage unlockFocus];
915                 
916                 [workingImage release];
917         }
918 #else   // This was an attempt at explicitly giving the NSImage an NSBitmapImageRep
919         // and drawing to that NSBitmapImageRep.  It doesn't work.  (See comments
920         // in -initWithThumbnailsOfImage:)
921         
922 //    // Create a new 32-bit RGBA bitmap that is width x width pixels.
923     originalImageRep = [image bestRepresentationForDevice:nil];
924     newImage = [[NSImage alloc] initWithSize:NSMakeSize(iconWidth,iconWidth)];
925     [newImage setDataRetained:YES];
926 //    [newImage setCachedSeparately:YES];
927     newBitmapImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
928         pixelsWide:iconWidth
929         pixelsHigh:iconWidth
930 //      bitsPerSample:8
931 //      samplesPerPixel:4
932         bitsPerSample:[originalImageRep bitsPerSample]
933         samplesPerPixel:[(NSBitmapImageRep*)originalImageRep samplesPerPixel]
934         hasAlpha:[originalImageRep hasAlpha]
935         isPlanar:NO
936         colorSpaceName:[originalImageRep colorSpaceName]
937         bytesPerRow:0
938         bitsPerPixel:0];
939     [newImage addRepresentation:newBitmapImageRep];
940     [newImage setScalesWhenResized:YES];
941     [newBitmapImageRep release];
942 //    bitmapData = [newBitmapImageRep bitmapData];
943 //    if (bitmapData)
944 //        memset( bitmapData, 128, iconWidth * iconWidth * 4 );
945     // Copy the original image into the new bitmap, rescaling it to fit.
946 //    [newImage lockFocus];
947     [newImage lockFocusOnRepresentation:newBitmapImageRep];
948 //    [image compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
949 //    iconRect.origin.x = iconRect.origin.y = 0;
950 //    iconRect.size.width = iconRect.size.height = iconWidth;
951 //    [[NSColor clearColor] set];
952 //    NSRectFill( iconRect );
953     [workingImage compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
954     [newImage unlockFocus];
955     
956     [workingImage release];
957 #endif
959     // Return the new image!
960     return [newImage autorelease];
963 + (Handle) get32BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
965     Handle hRawData;
966     char* pRawData;
967     Size rawDataSize;
968     unsigned char* pSrc;
969     char* pDest;
970     int x, y;
971     unsigned char alphaByte;
972     float oneOverAlpha;
973     
974     // Get information about the bitmapImageRep.
975     int pixelsWide      = [bitmapImageRep pixelsWide];
976     int pixelsHigh      = [bitmapImageRep pixelsHigh];
977     int bitsPerSample   = [bitmapImageRep bitsPerSample];
978     int samplesPerPixel = [bitmapImageRep samplesPerPixel];
979     int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
980 //    BOOL hasAlpha       = [bitmapImageRep hasAlpha];
981     BOOL isPlanar       = [bitmapImageRep isPlanar];
982 //    int numberOfPlanes  = [bitmapImageRep numberOfPlanes];
983     int bytesPerRow     = [bitmapImageRep bytesPerRow];
984 //    int bytesPerPlane   = [bitmapImageRep bytesPerPlane];
985     unsigned char* bitmapData = [bitmapImageRep bitmapData];
987     // Make sure bitmap has the required dimensions.
988     if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
989         return NULL;
990         
991     // So far, this code only handles non-planar 32-bit RGBA and 24-bit RGB source bitmaps.
992     // This could be made more flexible with some additional programming to accommodate other possible
993     // formats...
994     if (isPlanar)
995         {
996                 NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
997                 return NULL;
998         }
999     if (bitsPerSample != 8)
1000         {
1001                 NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d", bitsPerSample);
1002                 return NULL;
1003         }
1005         if (((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixel == 4) && (bitsPerPixel == 32)))
1006         {
1007                 rawDataSize = pixelsWide * pixelsHigh * 4;
1008                 hRawData = NewHandle( rawDataSize );
1009                 if (hRawData == NULL)
1010                         return NULL;
1011                 pRawData = *hRawData;
1012         
1013                 pSrc = bitmapData;
1014                 pDest = pRawData;
1015                 
1016                 if (bitsPerPixel == 32) {
1017                         for (y = 0; y < pixelsHigh; y++) {
1018                                 pSrc = bitmapData + y * bytesPerRow;
1019                                         for (x = 0; x < pixelsWide; x++) {
1020                                                 // Each pixel is 3 bytes of RGB data, followed by 1 byte of
1021                                                 // alpha.  The RGB values are premultiplied by the alpha (so
1022                                                 // that Quartz can save time when compositing the bitmap to a
1023                                                 // destination), and we undo this premultiplication (with some
1024                                                 // lossiness unfortunately) when retrieving the bitmap data.
1025                                                 *pDest++ = alphaByte = *(pSrc+3);
1026                                                 if (alphaByte) {
1027                                                         oneOverAlpha = 255.0f / (float)alphaByte;
1028                                                         *pDest++ = *(pSrc+0) * oneOverAlpha;
1029                                                         *pDest++ = *(pSrc+1) * oneOverAlpha;
1030                                                         *pDest++ = *(pSrc+2) * oneOverAlpha;
1031                                                 } else {
1032                                                         *pDest++ = 0;
1033                                                         *pDest++ = 0;
1034                                                         *pDest++ = 0;
1035                                                 }
1036                                                 pSrc+=4;
1037                                 }
1038                         }
1039                 } else if (bitsPerPixel == 24) {
1040                         for (y = 0; y < pixelsHigh; y++) {
1041                                 pSrc = bitmapData + y * bytesPerRow;
1042                                 for (x = 0; x < pixelsWide; x++) {
1043                                         *pDest++ = 0;
1044                                         *pDest++ = *pSrc++;
1045                                         *pDest++ = *pSrc++;
1046                                         *pDest++ = *pSrc++;
1047                                 }
1048                         }
1049                 }
1050         }
1051         else
1052         {
1053                 NSLog(@"get32BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %", samplesPerPixel, bitsPerPixel);
1054                 return NULL;
1055         }
1057     return hRawData;
1060 + (Handle) get8BitDataFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
1062     Handle hRawData;
1063     char* pRawData;
1064     Size rawDataSize;
1065     unsigned char* pSrc;
1066     char* pDest;
1067     int x, y;
1068         
1069     // Get information about the bitmapImageRep.
1070     int pixelsWide      = [bitmapImageRep pixelsWide];
1071     int pixelsHigh      = [bitmapImageRep pixelsHigh];
1072     int bitsPerSample   = [bitmapImageRep bitsPerSample];
1073     int samplesPerPixel = [bitmapImageRep samplesPerPixel];
1074     int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
1075     BOOL isPlanar       = [bitmapImageRep isPlanar];
1076     int bytesPerRow     = [bitmapImageRep bytesPerRow];
1077     unsigned char* bitmapData = [bitmapImageRep bitmapData];
1078     
1079     // Make sure bitmap has the required dimensions.
1080     if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
1081         return NULL;
1082         
1083     // So far, this code only handles non-planar 32-bit RGBA and 24-bit RGB source bitmaps.
1084     // This could be made more flexible with some additional programming...
1085     if (isPlanar)
1086         {
1087                 NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
1088                 return NULL;
1089         }
1090     if (bitsPerSample != 8)
1091         {
1092                 NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d", bitsPerSample);
1093                 return NULL;
1094         }
1095         
1096         if (((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixel == 4) && (bitsPerPixel == 32)))
1097         {
1098                 CGDirectPaletteRef cgPal;
1099                 CGDeviceColor cgCol;
1101                 rawDataSize = pixelsWide * pixelsHigh;
1102                 hRawData = NewHandle( rawDataSize );
1103                 if (hRawData == NULL)
1104                         return NULL;
1105                 pRawData = *hRawData;
1106                 
1107                 cgPal = CGPaletteCreateDefaultColorPalette();
1108                 
1109                 pSrc = bitmapData;
1110                 pDest = pRawData;
1111                 if (bitsPerPixel == 32) {
1112                         for (y = 0; y < pixelsHigh; y++) {
1113                                 pSrc = bitmapData + y * bytesPerRow;
1114                                 for (x = 0; x < pixelsWide; x++) {
1115                                         cgCol.red = ((float)*(pSrc)) / 255;
1116                                         cgCol.green = ((float)*(pSrc+1)) / 255;
1117                                         cgCol.blue = ((float)*(pSrc+2)) / 255;
1118         
1119                                         *pDest++ = CGPaletteGetIndexForColor(cgPal, cgCol);
1120         
1121                                         pSrc+=4;
1122                                 }
1123                         }
1124                 } else if (bitsPerPixel == 24) {
1125                         for (y = 0; y < pixelsHigh; y++) {
1126                                 pSrc = bitmapData + y * bytesPerRow;
1127                                 for (x = 0; x < pixelsWide; x++) {
1128                                         cgCol.red = ((float)*(pSrc)) / 255;
1129                                         cgCol.green = ((float)*(pSrc+1)) / 255;
1130                                         cgCol.blue = ((float)*(pSrc+2)) / 255;
1131         
1132                                         *pDest++ = CGPaletteGetIndexForColor(cgPal, cgCol);
1133         
1134                                         pSrc+=3;
1135                                 }
1136                         }
1137                 }
1138                 
1139                 CGPaletteRelease(cgPal);
1140         }
1141         else
1142         {
1143                 NSLog(@"get8BitDataFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %", samplesPerPixel, bitsPerPixel);
1144                 return NULL;
1145         }
1146         
1147     return hRawData;
1150 + (Handle) get8BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
1152     Handle hRawData;
1153         char* pRawData;
1154     Size rawDataSize;
1155     unsigned char* pSrc;
1156     char* pDest;
1157     int x, y;
1158     
1159     // Get information about the bitmapImageRep.
1160     int pixelsWide      = [bitmapImageRep pixelsWide];
1161     int pixelsHigh      = [bitmapImageRep pixelsHigh];
1162     int bitsPerSample   = [bitmapImageRep bitsPerSample];
1163     int samplesPerPixel = [bitmapImageRep samplesPerPixel];
1164     int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
1165 //    BOOL hasAlpha       = [bitmapImageRep hasAlpha];
1166     BOOL isPlanar       = [bitmapImageRep isPlanar];
1167 //    int numberOfPlanes  = [bitmapImageRep numberOfPlanes];
1168     int bytesPerRow     = [bitmapImageRep bytesPerRow];
1169 //    int bytesPerPlane   = [bitmapImageRep bytesPerPlane];
1170     unsigned char* bitmapData = [bitmapImageRep bitmapData];
1172     // Make sure bitmap has the required dimensions.
1173     if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
1174                 return NULL;
1175         
1176     // So far, this code only handles non-planar 32-bit RGBA, 24-bit RGB and 8-bit grayscale source bitmaps.
1177     // This could be made more flexible with some additional programming...
1178     if (isPlanar)
1179         {
1180                 NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
1181                 return NULL;
1182         }
1183     if (bitsPerSample != 8)
1184         {
1185                 NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerSample == %d", bitsPerSample);
1186                 return NULL;
1187         }
1188         
1189         if (((samplesPerPixel == 1) && (bitsPerPixel == 8)) || ((samplesPerPixel == 3) && (bitsPerPixel == 24)) || ((samplesPerPixel == 4) && (bitsPerPixel == 32)))
1190         {
1191                 rawDataSize = pixelsWide * pixelsHigh;
1192                 hRawData = NewHandle( rawDataSize );
1193                 if (hRawData == NULL)
1194                         return NULL;
1195                 pRawData = *hRawData;
1196         
1197                 pSrc = bitmapData;
1198                 pDest = pRawData;
1199                 
1200                 if (bitsPerPixel == 32) {
1201                         for (y = 0; y < pixelsHigh; y++) {
1202                                 pSrc = bitmapData + y * bytesPerRow;
1203                                 for (x = 0; x < pixelsWide; x++) {
1204                                         pSrc += 3;
1205                                         *pDest++ = *pSrc++;
1206                                 }
1207                         }
1208                 }
1209                 else if (bitsPerPixel == 24) {
1210                         memset( pDest, 255, rawDataSize );
1211                 }
1212                 else if (bitsPerPixel == 8) {
1213                         for (y = 0; y < pixelsHigh; y++) {
1214                                 memcpy( pDest, pSrc, pixelsWide );
1215                                 pSrc += bytesPerRow;
1216                                 pDest += pixelsWide;
1217                         }
1218                 }
1219         }
1220         else
1221         {
1222                 NSLog(@"get8BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to samplesPerPixel == %d, bitsPerPixel == %", samplesPerPixel, bitsPerPixel);
1223                 return NULL;
1224         }
1226     return hRawData;
1229 // NOTE: This method hasn't been fully tested yet.
1230 + (Handle) get1BitMaskFromBitmapImageRep:(NSBitmapImageRep*)bitmapImageRep requiredPixelSize:(int)requiredPixelSize
1232     Handle hRawData;
1233     char* pRawData;
1234     Size rawDataSize;
1235     unsigned char* pSrc;
1236     char* pDest;
1237     int x, y;
1238     unsigned char maskByte;
1239     
1240     // Get information about the bitmapImageRep.
1241     int pixelsWide      = [bitmapImageRep pixelsWide];
1242     int pixelsHigh      = [bitmapImageRep pixelsHigh];
1243     int bitsPerSample   = [bitmapImageRep bitsPerSample];
1244     int samplesPerPixel = [bitmapImageRep samplesPerPixel];
1245     int bitsPerPixel    = [bitmapImageRep bitsPerPixel];
1246 //    BOOL hasAlpha       = [bitmapImageRep hasAlpha];
1247     BOOL isPlanar       = [bitmapImageRep isPlanar];
1248 //    int numberOfPlanes  = [bitmapImageRep numberOfPlanes];
1249     int bytesPerRow     = [bitmapImageRep bytesPerRow];
1250 //    int bytesPerPlane   = [bitmapImageRep bytesPerPlane];
1251     unsigned char* bitmapData = [bitmapImageRep bitmapData];
1252         
1253     // Make sure bitmap has the required dimensions.
1254     if (pixelsWide != requiredPixelSize || pixelsHigh != requiredPixelSize)
1255                 return NULL;
1256         
1257     // So far, this code only handles non-planar 32-bit RGBA, 24-bit RGB, 8-bit grayscale, and 1-bit source bitmaps.
1258     // This could be made more flexible with some additional programming...
1259     if (isPlanar)
1260         {
1261                 NSLog(@"get1BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to isPlanar == YES");
1262                 return NULL;
1263         }
1264         
1265         if (((bitsPerPixel == 1) && (samplesPerPixel == 1) && (bitsPerSample == 1)) || ((bitsPerPixel == 8) && (samplesPerPixel == 1) && (bitsPerSample == 8)) ||
1266                 ((bitsPerPixel == 24) && (samplesPerPixel == 3) && (bitsPerSample == 8)) || ((bitsPerPixel == 32) && (samplesPerPixel == 4) && (bitsPerSample == 8)))
1267         {
1268                 rawDataSize = (pixelsWide * pixelsHigh)/4;
1269                 hRawData = NewHandle( rawDataSize );
1270                 if (hRawData == NULL)
1271                         return NULL;
1272                 pRawData = *hRawData;
1273         
1274                 pSrc = bitmapData;
1275                 pDest = pRawData;
1276                 
1277                 if (bitsPerPixel == 32) {
1278                         for (y = 0; y < pixelsHigh; y++) {
1279                                 pSrc = bitmapData + y * bytesPerRow;
1280                                 for (x = 0; x < pixelsWide; x += 8) {
1281                                         maskByte = 0;
1282                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x80 : 0; pSrc += 4;
1283                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x40 : 0; pSrc += 4;
1284                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x20 : 0; pSrc += 4;
1285                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x10 : 0; pSrc += 4;
1286                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x08 : 0; pSrc += 4;
1287                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x04 : 0; pSrc += 4;
1288                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x02 : 0; pSrc += 4;
1289                                         maskByte |= (*(unsigned*)pSrc & 0xff) ? 0x01 : 0; pSrc += 4;
1290                                         *pDest++ = maskByte;
1291                                 }
1292                         }
1293                 }
1294                 else if (bitsPerPixel == 24) {
1295                         memset( pDest, 255, rawDataSize );
1296                 }
1297                 else if (bitsPerPixel == 8) {
1298                         for (y = 0; y < pixelsHigh; y++) {
1299                                 pSrc = bitmapData + y * bytesPerRow;
1300                                 for (x = 0; x < pixelsWide; x += 8) {
1301                                         maskByte = 0;
1302                                         maskByte |= *pSrc++ ? 0x80 : 0;
1303                                         maskByte |= *pSrc++ ? 0x40 : 0;
1304                                         maskByte |= *pSrc++ ? 0x20 : 0;
1305                                         maskByte |= *pSrc++ ? 0x10 : 0;
1306                                         maskByte |= *pSrc++ ? 0x08 : 0;
1307                                         maskByte |= *pSrc++ ? 0x04 : 0;
1308                                         maskByte |= *pSrc++ ? 0x02 : 0;
1309                                         maskByte |= *pSrc++ ? 0x01 : 0;
1310                                         *pDest++ = maskByte;
1311                                 }
1312                         }
1313                 }
1314                 else if (bitsPerPixel == 1) {
1315                         for (y = 0; y < pixelsHigh; y++) {
1316                                 memcpy( pDest, pSrc, pixelsWide / 8 );
1317                                 pDest += pixelsWide / 8;
1318                                 pSrc += bytesPerRow;
1319                         }
1320                 }
1321                 
1322                 memcpy( pRawData+(pixelsWide*pixelsHigh)/8, pRawData, (pixelsWide*pixelsHigh)/8 );
1323         }
1324         else
1325         {
1326                 NSLog(@"get1BitMaskFromBitmapImageRep:requiredPixelSize: returning NULL due to bitsPerPixel == %d, samplesPerPixel== %d, bitsPerSample == %d", bitsPerPixel, samplesPerPixel, bitsPerSample);
1327                 return NULL;
1328         }
1329         
1330     return hRawData;
1333 - (BOOL) addResourceType:(OSType)type asResID:(int)resID 
1335     Handle hIconRes = NewHandle(0);
1336     OSErr err;
1338     err = GetIconFamilyData( hIconFamily, type, hIconRes );
1340     if( !GetHandleSize(hIconRes) || err != noErr )
1341         return NO;
1343     AddResource( hIconRes, type, resID, "\p" );
1345     return YES;
1348 @end
1350 // Methods for interfacing with the Carbon Scrap Manager (analogous to and
1351 // interoperable with the Cocoa Pasteboard).
1353 @implementation IconFamily (ScrapAdditions)
1355 + (BOOL) canInitWithScrap
1357     ScrapRef scrap = NULL;
1358     ScrapFlavorInfo* scrapInfos = NULL;
1359     UInt32 numInfos = 0;
1360     int i = 0;
1361     BOOL canInit = NO;
1363     GetCurrentScrap(&scrap);
1365     GetScrapFlavorCount(scrap,&numInfos);
1366     scrapInfos = malloc( sizeof(ScrapFlavorInfo)*numInfos );
1368     GetScrapFlavorInfoList(scrap, &numInfos, scrapInfos);
1370     for( i=0; i<numInfos; i++ )
1371     {
1372         if( scrapInfos[i].flavorType == 'icns' )
1373             canInit = YES;
1374     }
1376     free( scrapInfos );
1378     return canInit;
1381 + (IconFamily*) iconFamilyWithScrap
1383     return [[[IconFamily alloc] initWithScrap] autorelease];
1386 - initWithScrap
1388     Handle storageMem = NULL;
1389     Size amountMem = 0;
1390     ScrapRef scrap;
1392     if((self = [super init]))
1393     {
1394         GetCurrentScrap(&scrap);
1396         GetScrapFlavorSize( scrap, 'icns', &amountMem );
1398         storageMem = NewHandle(amountMem);
1400         GetScrapFlavorData( scrap, 'icns', &amountMem, *storageMem );
1402         hIconFamily = (IconFamilyHandle)storageMem;
1403     }
1404     return self;
1407 - (BOOL) putOnScrap
1409     ScrapRef scrap = NULL;
1411     ClearCurrentScrap();
1412     GetCurrentScrap(&scrap);
1414     HLock((Handle)hIconFamily);
1415     PutScrapFlavor( scrap, 'icns', kScrapFlavorMaskNone, GetHandleSize((Handle)hIconFamily), *hIconFamily);
1416     HUnlock((Handle)hIconFamily);
1417     return YES;
1420 @end
1421 #endif